package org.budo.dubbo.protocol.http.view.render.budo;

import org.budo.dubbo.protocol.http.aop.permission.app.AppPermissionCheckException;
import org.budo.dubbo.protocol.http.aop.permission.user.UserPermissionCheckException;
import org.budo.dubbo.protocol.http.exception.remoting.ArgumentTimeFormatException;
import org.budo.dubbo.protocol.http.exception.remoting.ArgumentTypeMismatchException;
import org.budo.dubbo.protocol.http.exception.remoting.ArrayArgumentMismatchException;
import org.budo.dubbo.protocol.http.exception.remoting.AuthenticationCheckException;
import org.budo.dubbo.protocol.http.exception.remoting.AuthorizeFailException;
import org.budo.dubbo.protocol.http.exception.remoting.EnumArgumentMismatchException;
import org.budo.dubbo.protocol.http.exception.remoting.InterfaceMissMatchException;
import org.budo.dubbo.protocol.http.exception.remoting.MethodMissMatchException;
import org.budo.dubbo.protocol.http.exception.remoting.SignCheckException;
import org.budo.support.assertion.AssertException;
import org.budo.support.exception.business.BusinessException;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.slf4j.Slf4j;
import org.budo.validation.exception.AbstractBudoValidationException;
import org.slf4j.Logger;

import com.alibaba.dubbo.rpc.RpcException;

/**
 * @author lmw
 */
public class ExceptionRender {
    private static final Logger log = Slf4j.getLogger();

    private AuthenticationCheckExceptionRender authenticationCheckExceptionRender = new AuthenticationCheckExceptionRender();

    private SignCheckExceptionRender signCheckExceptionRender = new SignCheckExceptionRender();

    private ValidationExceptionRender validationExceptionRender = new ValidationExceptionRender();

    private AssertExceptionRender assertExceptionRender = new AssertExceptionRender();

    public ApiError buildApiError(Throwable exception) {
        if (exception instanceof InterfaceMissMatchException) {
            return ApiError.INTERFACE_MISS_MATCH;
        }

        if (exception instanceof MethodMissMatchException) {
            return ApiError.METHOD_MISS_MATCH;
        }

        if (exception instanceof AuthenticationCheckException) {
            return authenticationCheckExceptionRender.buildApiError((AuthenticationCheckException) exception);
        }

        if (exception instanceof SignCheckException) {
            return signCheckExceptionRender.buildApiError((SignCheckException) exception);
        }

        if (exception instanceof AbstractBudoValidationException) { // 注解参数检查
            return validationExceptionRender.buildApiError((AbstractBudoValidationException) exception);
        }

        if (exception instanceof AssertException) {
            return assertExceptionRender.buildApiError((AssertException) exception);
        }

        if (exception instanceof UserPermissionCheckException) {
            return ApiError.USER_PERMISSION_ERROR.withDescription(exception.getMessage());
        }

        if (exception instanceof AppPermissionCheckException) {
            return ApiError.APP_PERMISSION_ERROR.withDescription(exception.getMessage());
        }

        if (exception instanceof BusinessException) {
            return ApiError.BUSINESS_ERROR.withDescription(exception.getMessage());
        }

        if (exception instanceof ArrayArgumentMismatchException) { // 数组参数错误
            ArrayArgumentMismatchException ex = (ArrayArgumentMismatchException) exception;
            String description = "数组参数 " + ex.getParameterName() + "(值 " + ex.getArgumentValue() + ")错误, 需要传入一个数组";
            ApiError error = ApiError.ILLEGAL_ARRAY_PARAMETER.withDescription(description);
            return error;
        }

        if (exception instanceof EnumArgumentMismatchException) { // 枚举参数错误
            EnumArgumentMismatchException ex = (EnumArgumentMismatchException) exception;
            Class<?> parameterType = ex.getParameterType();
            String description = "枚举参数 " + ex.getParameterName() + "(值 " + ex.getArgumentValue() + ")错误, 需要" + "[" + StringUtil.join(parameterType.getEnumConstants()) + "]";
            ApiError error = ApiError.ILLEGAL_ENUM_PARAMETER.withDescription(description);
            return error;
        }

        if (exception instanceof ArgumentTypeMismatchException) { // 参数类型错误
            ArgumentTypeMismatchException ex = (ArgumentTypeMismatchException) exception;
            String argumentValueType = null == ex.getArgumentValue() ? null : ex.getArgumentValue().getClass().getName();
            String description = "参数 " + ex.getParameterName() + "(类型 " + argumentValueType + ")错误, 需要" + ex.getParameterType().getCanonicalName();
            ApiError error = ApiError.PARAMETER_TYPE_MISMATCH.withDescription(description);
            return error;
        }

        if (exception instanceof ArgumentTimeFormatException) {
            ArgumentTimeFormatException ex = (ArgumentTimeFormatException) exception;
            String description = "时间参数 " + ex.getParameterName() + "(#" + ex.getParameterIndex() + ") 格式错误, 只支持 " + StringUtil.join(ex.getTimeFormats());
            ApiError error = ApiError.NOT_SUPPORTED_TIME_FORMAT.withDescription(description);
            return error;
        }
        
        if(exception instanceof AuthorizeFailException) {
            AuthorizeFailException authorizeFailException=(AuthorizeFailException) exception;
            return authorizeFailException.getApiError();
        }

        // RpcException
        if (exception instanceof RpcException) {
            log.error("#104 RpcException=" + exception, exception);

            if (("" + exception).contains("No provider available for the service")) {
                return ApiError.NO_PROVIDER_ERROR;
            }

            return ApiError.RPC_ERROR;
        }

        // 其他异常
        log.error("#114 renderException else, exception=" + exception, exception);
        return ApiError.SERVER_ERROR;
    }
}